"""
    expense_crud.py
    ~~~~~~~~
    

    :author: Fufu, 2024/4/24
"""
from datetime import date, datetime
from typing import List, Tuple

from fastapi_async_sqlalchemy import db
from sqlalchemy import select, update, insert
from sqlmodel import select, desc

from app.crud.base_crud import CRUDBase
from app.crud.xy_cruds.log import log_crud
from app.models import ExpenseModel, CompanyModel, VendorModel, LineModel, BillingModel


class CRUDExpense(CRUDBase[ExpenseModel]):
    async def init_and_get_dates(self, dateline: date) -> List[ExpenseModel] | None:
        """按日期初始化数据并返回数据"""
        rows = await self.get_by_dateline(dateline)
        if rows:
            return rows

        # 当前日期数据记录为空时, 用最后一天的数据填充
        newest = await self.get_newest_data()
        if not newest:
            log_crud('error', 'Failed to get expense by newest data')
            return None

        # 获取最新模板数据
        rows = await self.get_by_dateline(newest.dateline)
        if not rows:
            return None

        # 更新数据日期字段, 清除主键 ID
        now = datetime.now()
        new_rows = []
        for row in rows:
            new_row = ExpenseModel(**row.__dict__)
            new_row.id = None
            new_row.create_time = now
            new_row.update_time = now
            new_row.dateline = dateline
            new_rows.append(new_row)
        return new_rows

    async def get_newest_data(self) -> ExpenseModel | None:
        """获取最新一条数据"""
        stmt = select(ExpenseModel).order_by(desc(ExpenseModel.dateline)).limit(1)
        res = await db.session.execute(stmt)
        return res.scalars().first()

    async def get_by_dateline(
            self,
            dateline: date,
    ) -> List[ExpenseModel] | None:
        try:
            stmt = select(ExpenseModel).where(ExpenseModel.dateline == dateline).order_by(ExpenseModel.company_id)
            res = await db.session.execute(stmt)
            return res.scalars().all()
        except Exception as e:
            log_crud('error', 'Failed to get expense by dateline: {}'.format(e), more=dateline)
            return None

    async def bulk_save_expense(self, rows: List[ExpenseModel]) -> bool:
        """批量更新 Model 数据行"""
        if not rows:
            return False

        try:
            fn = update if rows[0].id else insert
            await db.session.execute(fn(ExpenseModel), self.to_dicts(rows))
            return True
        except Exception as e:
            log_crud('error', 'Failed to bulk save expense model: {}'.format(e), more=rows)
            return False

    async def get_view_by_dateline(
            self,
            dateline: date,
    ) -> Tuple[ExpenseModel, CompanyModel, VendorModel, LineModel, BillingModel] | None:
        try:
            stmt = select(
                ExpenseModel,
                CompanyModel,
                VendorModel,
                LineModel,
                BillingModel,
            ).join(
                CompanyModel, ExpenseModel.company_id == CompanyModel.id,
            ).join(
                VendorModel, ExpenseModel.vendor_id == VendorModel.id,
            ).join(
                LineModel, ExpenseModel.line_id == LineModel.id,
            ).join(
                BillingModel, ExpenseModel.billing_id == BillingModel.id,
            ).where(
                ExpenseModel.dateline == dateline,
            ).order_by(
                desc(ExpenseModel.vendor_id),
                desc(ExpenseModel.line_id),
            )
            return await db.session.execute(stmt)
        except Exception as e:
            log_crud('error', 'Failed to get expense view by dateline: {}'.format(e), more=dateline)
            return None


crud_expense = CRUDExpense(ExpenseModel)
